// 路径：kernel/drivers/gpio/gpio-fst.c
/*
F1=GPIO0_B5=0x80
F2=GPIO3_A2=0x40
F3=GPIO3_A5=0x20
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/gpio/consumer.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
// #include <linux/asm/mach/map.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
// #include <linux/sched.h>
// #include <linux/kmod.h>
// #include <linux/kthread.h>

// #define CLINE_KERNEL (CLONE_FS | CLONE_FILES | CLONE_SIGHAND )
// struct task_struct *my_task_p=NULL;

#define DEV_NAME "gpiofst"
#define DEV_CNT 1

#define PIN_CNT 8

char kbuf[2] = {0, 0};

struct gpiofst_dev
{
    dev_t dev_id;           // 设备编号
    struct cdev cdev;       // 内核字符设备
    struct class *class;    // 设备类
    struct device *device;  // 设备
    int major;              // 主设备
    int minor;              // 从设备
    struct device_node *nd; // 设备节点
    int in_gpios[PIN_CNT];
    int in_gpio_cnt;
    int out_gpios[PIN_CNT];
    int out_gpio_cnt;
};

static struct gpiofst_dev gpiofst;

// static char readVal=0xff;

int gpiofst_open(struct inode *inode, struct file *file)
{
    file->private_data = &gpiofst;

    // printk("gpiofst open!\n");
    return 0;
}
/*int gpiofst_read_thread(void *arg)
{
    int i;
    unsigned char value;
    char lastval=0xff;
    char nowval;

    while(1)
    {
        for(i=0;i<gpiofst.in_gpio_cnt;i++)
        {
            nowval<<=1;
            value=gpio_get_value(gpiofst.in_gpios[i]);
            if(0!=value)
            {
                nowval|=1;
            }
        }
        if(nowval!=lastval)
        {
            printk("gpiofst key press: %d\n",nowval);
        }
        readVal=nowval;
        lastval=nowval;

        msleep(20);

        if(kthread_should_stop())
        {break;}
    }
    return 0;
}*/
ssize_t gpiofst_read(struct file *file, char __user *ubuf, size_t size, loff_t *offset)
{
    // struct gpiofst_dev *dev = file->private_data;
    unsigned char kval[2] = {0, 0};
    int i;
    unsigned char value;

    size = (size >= 1) ? 1 : 0;
    if (0 == size)
    {
        // printk("gpiofst too samll argument!\n");
        return -EINVAL;
    }

    for (i = 0; i < gpiofst.in_gpio_cnt; i++)
    {
        kval[0] <<= 1;
        value = gpio_get_value(gpiofst.in_gpios[i]);
        if (0 != value)
        {
            kval[0] |= 1;
        }
    }
    // kval[0]=readVal;

    if (copy_to_user(ubuf, kval, size))
    {
        // printk("gpiofst copy data to user fail!\n");
        return -EIO;
    }

    // printk("gpiofst read value:%d!\n",kval[0]);
    return size;
}

ssize_t gpiofst_write(struct file *file, const char __user *ubuf, size_t size, loff_t *offset)
{
    unsigned char value;
    int pin;
    int i;

    size = (size >= 2) ? 2 : 0;
    if (0 == size)
    {
        // printk("gpiofst too samll argument!\n");
        return -EINVAL;
    }

    if (copy_from_user(kbuf, ubuf, size))
    {
        // printk("gpiofst copy data form user fail!\n");
        return -EIO;
    }

    value = (kbuf[1] > 0) ? 1 : 0;

    pin = -1;
    for (i = 0; i < gpiofst.out_gpio_cnt; i++)
    {
        if (kbuf[0] == gpiofst.out_gpios[i])
        {
            pin = kbuf[0];
            break;
        }
    }
    if (-1 == pin)
    {
        // printk("gpiofst write pin %d not exist!\n",kbuf[0]);
        return -EIO;
    }

    gpio_set_value(pin, value);

    // printk("gpiofst write pin %d value %d!\n",pin, value);
    return size;
}

int gpiofst_close(struct inode *inode, struct file *file)
{
    // printk("gpiofst close!\n");
    return 0;
}

struct file_operations gpiofst_fops = {
    .owner = THIS_MODULE,
    .open = gpiofst_open,
    .read = gpiofst_read,
    .write = gpiofst_write,
    .release = gpiofst_close};

static int fstcdev_probe(struct platform_device *pdv)
{
    int ret = 0;
    // const char *str;
    int i;
    int pin;

    // 读取设备树节点的属性数据
    // 获取设备节点：gpiofst
    gpiofst.nd = of_find_node_by_path("/gpiofst");
    if (gpiofst.nd == NULL)
    {
        printk("gpiofst node find failed.\n");
        return -EINVAL;
    }
    printk("gpiofst node find ok.\n");

    // 获取 status 属性内容
    // ret = of_property_read_string(gpiofst.nd, "status", &str);
    // if (ret < 0)
    // {
    //     printk("gpiofst status read failed!\n");
    //     return -EINVAL;
    // }
    // printk("gpiofst status = %s\n", str);
    // if (strcmp(str, "okay"))
    // {
    //     return -EINVAL;
    // }

    // 获取 compatible 属性内容
    // ret = of_property_read_string(gpiofst.nd, "compatible", &str);
    // if (ret < 0)
    // {
    //     printk("gpiofst compatible read failed!\n");
    //     return -EINVAL;
    // }
    // printk("gpiofst compatible = %s\n", str);
    // if (strcmp(str, "fst,gpio"))
    // {
    //     printk("gpiofst: Compatible match failed\n");
    //     return -EINVAL;
    // }

    // 获取设备树中的 in-gpios 属性
    gpiofst.in_gpio_cnt = of_gpio_named_count(gpiofst.nd, "in-gpios");
    if (gpiofst.in_gpio_cnt <= 0)
    {
        printk("gpiofst get in-gpios failed.\n");
        return -EIO;
    }
    printk("gpiofst get %d nums from in-gpios!\n", gpiofst.in_gpio_cnt);
    // 向gpio子系统申请GPIO
    for (i = 0; i < gpiofst.in_gpio_cnt; i++)
    {
        pin = of_get_named_gpio(gpiofst.nd, "in-gpios", i);
        gpiofst.in_gpios[i] = pin;
        ret = gpio_request(pin, NULL);
        if (ret)
        {
            printk("gpiofst failed to request %d!\n", pin);
            return ret;
        }
        ret = gpio_direction_input(pin);
        if (ret < 0)
        {
            printk("gpiofst can't set %d input mode.\n", pin);
        }
    }

    // 获取设备树中的 out-gpios 属性
    gpiofst.out_gpio_cnt = of_gpio_named_count(gpiofst.nd, "out-gpios");
    if (gpiofst.out_gpio_cnt <= 0)
    {
        printk("gpiofst get out-gpios failed.\n");
        return -EIO;
    }
    printk("gpiofst get %d nums from out-gpios!\n", gpiofst.out_gpio_cnt);
    // 向gpio子系统申请GPIO
    for (i = 0; i < gpiofst.out_gpio_cnt; i++)
    {
        pin = of_get_named_gpio(gpiofst.nd, "out-gpios", i);
        gpiofst.out_gpios[i] = pin;
        ret = gpio_request(pin, NULL);
        if (ret)
        {
            printk("gpiofst failed to request %d!\n", pin);
            return ret;
        }
        ret = gpio_direction_output(pin, 0);
        if (ret < 0)
        {
            printk("gpiofst can't set %d output mode.\n", pin);
        }
    }

    // 注册字符设备驱动
    // 创建设备号
    if (gpiofst.major)
    {
        gpiofst.dev_id = MKDEV(gpiofst.major, 0);
        ret = register_chrdev_region(gpiofst.dev_id, DEV_CNT, DEV_NAME);
        if (ret < 0)
        {
            printk("gpiofst can't regsiter %s char driver [ret=%d]\n", DEV_NAME, ret);
            goto free_gpio;
        }
    }
    else
    {
        ret = alloc_chrdev_region(&gpiofst.dev_id, 0, DEV_CNT, DEV_NAME);
        if (ret < 0)
        {
            printk("gpiofst: %s can't alloc chrdev region, ret=%d.\n", DEV_NAME, ret);
            goto free_gpio;
        }
        gpiofst.major = MAJOR(gpiofst.dev_id);
        gpiofst.minor = MINOR(gpiofst.dev_id);
    }

    printk("gpiofst: dts major=%d, minor=%d.\n", gpiofst.major, gpiofst.minor);
    // 关联结构体
    gpiofst.cdev.owner = THIS_MODULE;
    cdev_init(&gpiofst.cdev, &gpiofst_fops);

    ret = cdev_add(&gpiofst.cdev, gpiofst.dev_id, DEV_CNT);
    if (ret < 0)
    {
        goto del_unregister;
    }

    gpiofst.class = class_create(THIS_MODULE, DEV_NAME);
    if (IS_ERR(gpiofst.class))
    {
        goto del_cdev;
    }

    gpiofst.device = device_create(gpiofst.class, NULL, gpiofst.dev_id, NULL, DEV_NAME);
    if (IS_ERR(gpiofst.device))
    {
        goto destroy_class;
    }

    // my_task_p=kthread_run(gpiofst_read_thread,NULL,"gpiofst_read_thread");

    printk("gpiofst init ...\n");
    return 0;
destroy_class:
    class_destroy(gpiofst.class);
del_cdev:
    cdev_del(&gpiofst.cdev);
del_unregister:
    unregister_chrdev_region(gpiofst.dev_id, DEV_CNT);
free_gpio:
    for (i = 0; i < gpiofst.in_gpio_cnt; i++)
    {
        gpio_free(gpiofst.in_gpios[i]);
    }
    for (i = 0; i < gpiofst.out_gpio_cnt; i++)
    {
        gpio_free(gpiofst.out_gpios[i]);
    }

    return -EIO;
}
static int fstcdev_remove(struct platform_device *pdv)
{
    int i;

    printk("gpiofst exit ...\n");

    // kthread_stop(my_task_p);

    cdev_del(&gpiofst.cdev);

    unregister_chrdev_region(gpiofst.dev_id, DEV_CNT);

    device_destroy(gpiofst.class, gpiofst.dev_id);

    class_destroy(gpiofst.class);

    for (i = 0; i < gpiofst.in_gpio_cnt; i++)
    {
        gpio_free(gpiofst.in_gpios[i]);
    }
    for (i = 0; i < gpiofst.out_gpio_cnt; i++)
    {
        gpio_free(gpiofst.out_gpios[i]);
    }
    return 0;
}

static const struct of_device_id gpiofst_match_table[] = {
    {.compatible = "fst,gpio"},
    {},
};

static struct platform_driver fst_gpio = {
    .remove = fstcdev_remove,
    .probe = fstcdev_probe,
    .driver = {
        .owner = THIS_MODULE,
        .of_match_table = gpiofst_match_table,
        .name = "gpiofst-platform"},
};

static int __init gpiofst_init(void)
{
    return platform_driver_register(&fst_gpio);
}

static void __exit gpiofst_exit(void)
{
    platform_driver_unregister(&fst_gpio);
}

module_init(gpiofst_init);
module_exit(gpiofst_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("fst");
